use crate::{
    common,
    tables::role::{Role, TbRole},
};
use actix_web::{delete, get, post, put, web, HttpResponse, Responder};
use once_cell::sync::Lazy;
use rbatis::rbdc;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::{collections::HashMap, sync::RwLock};

static ROLE_CACHE: Lazy<RwLock<HashMap<u64, TbRole>>> = Lazy::new(|| RwLock::new(HashMap::new()));

pub async fn init_roles() -> Result<(), rbdc::Error> {
    let roles = TbRole::get_all_role().await?;

    let role_cache = &mut ROLE_CACHE.write().unwrap();
    roles.into_iter().for_each(|r| {
        role_cache.insert(r.id.unwrap(), r);
    });

    Ok(())
}

async fn insert_role(data: &mut TbRole) -> Result<(), rbdc::Error> {
    TbRole::insert_role(data).await?;

    let role_cache = &mut ROLE_CACHE.write().unwrap();
    role_cache.insert(data.id.unwrap(), data.to_owned());

    Ok(())
}

async fn update_role(data: &TbRole) -> Result<(), rbdc::Error> {
    TbRole::update_role(data).await?;

    let role_cache = &mut ROLE_CACHE.write().unwrap();
    role_cache.insert(data.id.unwrap(), data.to_owned());

    Ok(())
}

async fn delete_role(id: u64) -> Result<(), rbdc::Error> {
    TbRole::delete_role(id).await?;

    let role_cache = &mut ROLE_CACHE.write().unwrap();
    role_cache.remove(&id);

    Ok(())
}

pub fn get_role(id: u64) -> Option<TbRole> {
    let role_cache = ROLE_CACHE.read().unwrap();
    role_cache.get(&id).map(|r| r.to_owned())
}

#[derive(Debug, Serialize, Deserialize, Default)]
pub struct Get {
    id: Option<u64>,
    name: Option<String>,
}

#[get("")]
async fn get(page: web::Query<common::QueryPage>, query: web::Query<Get>) -> impl Responder {
    common::ResData::<Role>::from_page(TbRole::get_roles(&page, &query).await).resp_json()
}

fn check_role(data: &web::Json<Role>) -> Result<(), &str> {
    if data.name.is_empty() {
        return Err("角色名不能为空");
    }

    Ok(())
}

#[post("")]
async fn post(data: web::Json<Role>) -> impl Responder {
    let check = check_role(&data);
    if let Err(e) = check {
        return HttpResponse::Ok().json(json!({
            "success": false,
            "errorMessage": e
        }));
    }

    HttpResponse::Ok().json(match insert_role(&mut TbRole::from(data.0)).await {
        Ok(_) => json!({
            "success": true,
            "errorMessage": "操作成功"
        }),
        Err(e) => json!({
            "success": false,
            "errorMessage": e.to_string().as_str()
        }),
    })
}

#[put("")]
async fn put(data: web::Json<Role>) -> impl Responder {
    let check = check_role(&data);
    if let Err(e) = check {
        return HttpResponse::Ok().json(json!({
            "success": false,
            "errorMessage": e
        }));
    }

    HttpResponse::Ok().json(match update_role(&TbRole::from(data.0)).await {
        Ok(_) => json!({
            "success": true,
            "errorMessage": "操作成功"
        }),
        Err(e) => json!({
            "success": false,
            "errorMessage": e.to_string().as_str()
        }),
    })
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Delete {
    pub id: u64,
}

#[delete("")]
async fn delete(data: web::Json<Delete>) -> HttpResponse {
    if data.id == 1 {
        return HttpResponse::Ok().json(json!({
            "success": false,
            "errorMessage": "该角色不能删除"
        }));
    }

    HttpResponse::Ok().json(match delete_role(data.id).await {
        Ok(_) => json!({
            "success": true,
            "errorMessage": "操作成功"
        }),
        Err(e) => json!({
            "success": false,
            "errorMessage": e.to_string().as_str()
        }),
    })
}

pub fn routes(cfg: &mut web::ServiceConfig) {
    cfg.service(
        web::scope("/role")
            .service(get)
            .service(post)
            .service(put)
            .service(delete),
    );
}

#[cfg(test)]
pub mod tests {
    use crate::{
        common,
        handlers::role::{self, Get},
        utils::mysql,
    };
    use actix_web::{test, App};
    use serde_json::{json, Value};

    #[actix_web::test]
    async fn test_get_roles() {
        mysql::init_pool().await;

        super::init_roles().await.expect("init roles failed");

        let app = test::init_service(App::new().configure(role::routes)).await;

        let page = &mut common::QueryPage::default();
        page.page_size = Some(5);
        let page = serde_urlencoded::to_string(page).unwrap();
        let query = &mut Get::default();
        query.name = Some("管理员".into());
        let query = serde_urlencoded::to_string(query).unwrap();

        let req = test::TestRequest::get()
            .uri(&("/role?".to_owned() + page.as_str() + "&" + query.as_str()))
            .to_request();

        let resp = test::call_service(&app, req).await;
        let status = resp.status();
        println!("resp status is {:?}", status);
        let resp_body = test::read_body(resp).await;
        let json_value: Value = serde_json::from_slice(&resp_body).unwrap();
        println!("resp body is {}", json_value);
        assert!(status.is_success());
    }

    #[actix_web::test]
    async fn test_add_role() {
        mysql::init_pool().await;

        super::init_roles().await.expect("init roles failed");

        let app = test::init_service(App::new().configure(role::routes)).await;

        let req = test::TestRequest::post()
            .uri("/role")
            .set_json(json!({"name": "fake police"}))
            .to_request();

        let resp = test::call_service(&app, req).await;
        let status = resp.status();
        println!("resp status is {:?}", status);
        let resp_body = test::read_body(resp).await;
        let json_value: Value = serde_json::from_slice(&resp_body).unwrap();
        println!("resp body is {:?}", json_value);
        assert!(status.is_success());
    }

    #[actix_web::test]
    async fn test_update_role() {
        mysql::init_pool().await;

        super::init_roles().await.expect("init roles failed");

        let app = test::init_service(App::new().configure(role::routes)).await;

        let req = test::TestRequest::put()
            .uri("/role")
            .set_json(json!({"id": 3, "name": "测试用户"}))
            .to_request();

        let resp = test::call_service(&app, req).await;
        let status = resp.status();
        println!("resp status is {:?}", status);
        let resp_body = test::read_body(resp).await;
        let json_value: Value = serde_json::from_slice(&resp_body).unwrap();
        println!("resp body is {:?}", json_value);
        assert!(status.is_success());
    }

    #[actix_web::test]
    async fn test_delete_role() {
        mysql::init_pool().await;

        super::init_roles().await.expect("init roles failed");

        let app = test::init_service(App::new().configure(role::routes)).await;

        let req = test::TestRequest::delete()
            .uri("/role")
            .set_json(json!({"id": 4}))
            .to_request();

        let resp = test::call_service(&app, req).await;
        let status = resp.status();
        println!("resp status is {:?}", status);
        let resp_body = test::read_body(resp).await;
        let json_value: Value = serde_json::from_slice(&resp_body).unwrap();
        println!("resp body is {:?}", json_value);
        assert!(status.is_success());
    }
}
